`timescale 1ns / 1ns

module fc_top(
    input  wire         clk,
    input  wire         rst_n_fc,

    input wire [5:0]     count,  // input_ram读取数据地址的计数
    input wire [7:0]     addr_input_ram, // fifo_input写入RAM的地址
    input wire [1023:0]  data_input_ram, // fifo_input写入RAM的数据
    input wire           wren_input_ram, // fifo_input写入RAM的使能信号

    //第一层信号
    input  wire         start,
    output wire [7:0]  fc1_output_data_monitor,
    output wire         fc1_output_wren_monitor,
    output wire         fc1_done_monitor,
    
    //第二层信号
    output wire [7:0]  fc2_output_data_monitor,
    output wire         fc2_output_wren_monitor,
    output wire         fc2_done_monitor,
    
    //第三层信号
    output wire [7:0]  fc3_output_data_monitor,
    output wire         fc3_output_wren_monitor,
    output wire         fc3_done_monitor,
    output wire         done_o
    );
    
    /*输入信号*/
    wire [1:0]  input_data_addr;
    wire [1023:0] input_data;       //128(数量)*8(位宽)=1024bit

    /*第一层信号*/
    wire [8:0]   fc1_weight_addr;
    wire [1023:0] fc1_weight;       //128(数量)*8(位宽)=1024bit

    wire [6:0]   fc1_bias_addr; 
    wire [7:0]   fc1_bias;

    wire          fc1_output_wren;
    wire  [31:0]  fc1_output_data;
    wire  [6:0]   fc1_output_addr;

    wire [1023:0] fc1_mul_data1;
    wire [1023:0] fc1_mul_data2;
    wire [2047:0] fc1_mul_result;   //128(数量)*16(位宽)=2048bit

    wire          fc1_done;

    /*第二层信号*/
    wire [1:0]   fc2_input_addr;
    wire [1023:0] fc2_input_data;   //32(数量)*32(位宽)=1024bit
    
    wire [6:0]   fc2_weight_addr;
    wire [255:0] fc2_weight;        //32(数量)*8(位宽)=256bit

    wire [4:0]   fc2_bias_addr; 
    wire [7:0]   fc2_bias;

    wire          fc2_output_wren;
    wire  [47:0]  fc2_output_data;
    wire  [4:0]   fc2_output_addr;

    wire [1023:0] fc2_mul_data1;
    wire [255:0] fc2_mul_data2;
    wire [1279:0] fc2_mul_result;   //32(数量)*40(位宽)=1280bit

    wire          fc2_done;

    /*第三层信号*/
    // fc_3 一次性读入所有输入数据, 不需要地址信号
    wire [1535:0] fc3_input_data;   //32(数量)*48(位宽)=1536bit

    wire [0:0]   fc3_weight_addr;
    wire [255:0] fc3_weight;        //32(数量)*8(位宽)=256bit

    wire [0:0]   fc3_bias_addr; 
    wire [7:0]   fc3_bias;

    wire          fc3_output_wren;
    wire  [63:0]  fc3_output_data;
    wire  [0:0]   fc3_output_addr;

    wire [1535:0] fc3_mul_data1;
    wire [255:0]  fc3_mul_data2;
    wire [1791:0] fc3_mul_result;   //32(数量)*56(位宽)=1792bit

    wire          fc3_done;

    /*monitor信号*/
    assign fc1_output_data_monitor = fc1_output_data_truncated[7:0];
    assign fc1_output_wren_monitor = fc1_output_wren;
    assign fc1_done_monitor = fc1_done;

    assign fc2_output_data_monitor = fc2_output_data_truncated[7:0];
    assign fc2_output_wren_monitor = fc2_output_wren;
    assign fc2_done_monitor = fc2_done;

    assign fc3_output_data_monitor = fc3_output_data_truncated[7:0];
    assign fc3_output_wren_monitor = fc3_output_wren;
    assign fc3_done_monitor = fc3_done;

    assign done_o = fc3_done;
    
    /*对start信号延迟得到start_pulse*/
    reg start_delay1, start_delay2;
    always @(posedge clk or negedge rst_n_fc) begin
        if (!rst_n_fc) begin
            start_delay1 <= 1'b0;
            start_delay2 <= 1'b0;
        end else begin
            start_delay1 <= start;
            start_delay2 <= start_delay1;
        end
    end
    wire start_pulse = start_delay1 & ~start_delay2;

    /*简单的状态机, 指示三层网络层是(1)否(0)正在运算
    规则: 相邻的网络层不能同时计算, 避免竞争bridge BRAM
    */
    reg [1:3] computing_state;
    always @(negedge clk or negedge rst_n_fc) begin
        if (!rst_n_fc) begin
            computing_state <= 3'b0;
        end else begin
            case (computing_state)
                3'b000: begin
                    if (start_pulse)
                        computing_state <= 3'b100; //第一层开始计算
                end
                3'b100: begin
                    if (fc1_done) //第一层计算完成
                        computing_state <= 3'b010; //第二层开始计算
                end
                3'b010: begin
                    if (fc2_done) //第二层计算完成
                        computing_state <= 3'b001; //第三层开始计算
                end
                3'b001: begin
                    if (fc3_done) //第三层计算完成
                        computing_state <= 3'b000; //空闲状态
                end
                default: computing_state <= 3'b000;
            endcase
        end
    end

    // 第一层-第二层桥接地址逻辑: 根据当前计算层选择地址
    // 这里必须写成组合逻辑，否则会引入一个时钟周期的延迟，导致数据错位
    // Vivado BRAM 在非对称宽度配置下，采用小端方式拼接数据，先写入的数据在低位，后写入的数据在高位
    // 期望以地址0写入的数据在读取时位于高位，因此需要改写入地址，行内反序
    reg [6:0] bridge1_addr;
    always @(*) begin
        if (!rst_n_fc) begin
            bridge1_addr = 7'b0;
        end else if (computing_state[1]) begin
            // 写阶段：行内反序写入，blk=高2位，idx=低5位
            bridge1_addr = {fc1_output_addr[6:5], (5'd31 - fc1_output_addr[4:0])};
        end else if (computing_state[2]) begin
            // 读阶段：对齐到32字边界 0,32,64,96
            bridge1_addr = {fc2_input_addr, 5'b0};
        end else begin
            bridge1_addr = 7'b0;
        end
    end

    // !设置输入数据地址,总共256个地址进行循环使用
    wire [7:0] input_addr;

    assign input_addr = input_data_addr + 4 * count;
    
    /*第一层模块*/
    fc_input_ram u_fc_input_ram(
        .clka(clk),
        .wea(wren_input_ram),
        .addra(addr_input_ram),
        .dina(data_input_ram),
        .clkb(clk),
        .addrb(input_addr),
        .doutb(input_data)
    );

    fc1_weight_ram u_fc1_weight(
        .addra(fc1_weight_addr),
        .clka(clk),
        .douta(fc1_weight)
    );

    fc1_bias_ram u_fc1_bias(
        .addra(fc1_bias_addr),
        .clka(clk),
        .douta(fc1_bias)
    );

    mult_array1 u_mult_array1(
        .clk(clk),
        .rst_n(rst_n_fc),
        .data1(fc1_mul_data1),
        .data2(fc1_mul_data2),
        .result(fc1_mul_result)
    );

    fc_1 u_fc_1(
        .clk_i(clk),
        .rst_n_i(rst_n_fc),
        .start_i(start_pulse),
        .done_o(fc1_done),

        .input_data_addr_o(input_data_addr),
        .input_data_i(input_data),

        .weight_addr_o(fc1_weight_addr),
        .weight_i(fc1_weight),

        .bias_addr_o(fc1_bias_addr), 
        .bias_i(fc1_bias),

        .fc_output_wren_o(fc1_output_wren),
        .fc_output_data_o(fc1_output_data),
        .fc_output_addr_o(fc1_output_addr),

        .mul_data1_o(fc1_mul_data1),
        .mul_data2_o(fc1_mul_data2),
        .mul_result_i(fc1_mul_result)
    );


    // !进行第一层输出数据的截位(输出最大值位80805)
    // 右移10位
    wire [31:0] fc1_output_data_truncated;
    assign fc1_output_data_truncated = {24'b0, fc1_output_data[17:10]};

    // // !进行第一层4位截位(输出最大值位80805)
    // wire [31:0] fc1_output_data_truncated;
    // assign fc1_output_data_truncated = {28'b0, fc1_output_data[17:14]};

    /*第一层和第二层之间的桥接*/
    fc1_fc2_bridge u_fc1_fc2_bridge(
        .addra(bridge1_addr),
        .clka(clk),
        .dina(fc1_output_data_truncated),
        .douta(fc2_input_data),
        .rsta(~rst_n_fc),
        .wea(fc1_output_wren)
    );

    /*第二层模块*/
    fc2_weight_ram u_fc2_weight(
        .addra(fc2_weight_addr),
        .clka(clk),
        .douta(fc2_weight)
    );

    fc2_bias_ram u_fc2_bias(
        .addra(fc2_bias_addr),
        .clka(clk),
        .douta(fc2_bias)
    );

    mult_array2 u_mult_array2(
        .clk(clk),
        .rst_n(rst_n_fc),
        .data1(fc2_mul_data1),
        .data2(fc2_mul_data2),
        .result(fc2_mul_result)
    );

    fc_2 u_fc_2(
        .clk_i(clk),
        .rst_n_i(rst_n_fc),
        .start_i(fc1_done),
        .done_o(fc2_done),

        .input_data_addr_o(fc2_input_addr),
        .input_data_i(fc2_input_data),

        .weight_addr_o(fc2_weight_addr),
        .weight_i(fc2_weight),

        .bias_addr_o(fc2_bias_addr), 
        .bias_i(fc2_bias),

        .fc_output_wren_o(fc2_output_wren),
        .fc_output_data_o(fc2_output_data),
        .fc_output_addr_o(fc2_output_addr),

        .mul_data1_o(fc2_mul_data1),
        .mul_data2_o(fc2_mul_data2),
        .mul_result_i(fc2_mul_result)
    );

    // !进行第二层输出数据的截位(输出最大值位37683)
    // 右移9位
    wire [47:0] fc2_output_data_truncated;
    assign fc2_output_data_truncated = {40'b0, fc2_output_data[16:9]};

    // // !进行第二层的4位截位(输出最大值位1716)
    // wire [47:0] fc2_output_data_truncated;
    // assign fc2_output_data_truncated = {44'b0, fc2_output_data[11:8]};

    /*第二层和第三层之间的桥接*/
    fc2_fc3_bridge u_fc2_fc3_bridge(
        .clk(clk),
        .reset(~rst_n_fc),
        .in_data(fc2_output_data_truncated),
        .in_addr(fc2_output_addr),
        .in_wren(fc2_output_wren),
        .out_data(fc3_input_data)
    );

    /*第三层模块*/
    fc3_weight_ram u_fc3_weight(
        .addra(fc3_weight_addr),
        .clka(clk),
        .douta(fc3_weight)
    );

    fc3_bias_ram u_fc3_bias(
        .addra(fc3_bias_addr),
        .clka(clk),
        .douta(fc3_bias)
    );

    mult_array3 u_mult_array3(
        .clk(clk),
        .rst_n(rst_n_fc),
        .data1(fc3_mul_data1),
        .data2(fc3_mul_data2),
        .result(fc3_mul_result)
    );

    fc_3 u_fc_3(
        .clk_i(clk),
        .rst_n_i(rst_n_fc),
        .start_i(fc2_done),
        .done_o(fc3_done),

        .input_data_i(fc3_input_data),

        .weight_addr_o(fc3_weight_addr),
        .weight_i(fc3_weight),

        .bias_addr_o(fc3_bias_addr), 
        .bias_i(fc3_bias),

        .fc_output_wren_o(fc3_output_wren),
        .fc_output_data_o(fc3_output_data),
        .fc_output_addr_o(fc3_output_addr),

        .mul_data1_o(fc3_mul_data1),
        .mul_data2_o(fc3_mul_data2),
        .mul_result_i(fc3_mul_result)
    );

    // !进行第三层输出的截位(输出最大值为-32166)
    // 右移8位
    wire [63:0] fc3_output_data_truncated;
    assign fc3_output_data_truncated = {{9{fc3_output_data[63]}}, fc3_output_data[62:8]};

    // // !进行第三层输出的4位截位(输出最大值为2588)
    // wire [63:0] fc3_output_data_truncated;
    // assign fc3_output_data_truncated = {{9{fc3_output_data[63]}}, fc3_output_data[63:9]};

    wire[63:0] unused;
    fc_output_ram u_fc_output_ram(
        .addra(fc3_output_addr),
        .clka(clk),
        .dina(fc3_output_data_truncated),
        .douta(unused),
        .wea(fc3_output_wren)
    );

endmodule
